using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Microsoft.Xna.Framework.Graphics;
using Xfinity.Engine;
using System.Diagnostics;
using System.Text;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;

namespace Xfinity.Console
{
    /// <summary>
    /// A console.
    /// </summary>
    public static class StandardConsole
    {
        /// <summary>
        /// Toggles the console.
        /// </summary>
        /// <param name="commands">Arguments.</param>
        [BindCommand("ToggleConsole", Setting="ConsoleKey")]
        public static void ToggleConsole(string[] commands)
        {
            StandardConsole.DefaultConsole.Visible = !StandardConsole.DefaultConsole.Visible;
            StandardConsole.ConsoleShowing = !GraphicsHelper.DrawModels;
            StandardConsole.RedirectInputToConsole = !StandardConsole.RedirectInputToConsole;
        }
        private static List<string> currentTabMatches;

        /// <summary>
        /// The current tab matches.
        /// </summary>
        public static List<string> CurrentTabMatches
        {
            [DebuggerStepThrough]
            get { return currentTabMatches; }
            [DebuggerStepThrough]
            set { currentTabMatches = value; }
        }
        private static string tabCompletedLine = null;
        private static bool tabCompletion = false;

        /// <summary>
        /// Whether we're in tab completion mode.
        /// </summary>
        public static bool TabCompletion
        {
            [DebuggerStepThrough]
            get { return tabCompletion; }
            [DebuggerStepThrough]
            set { tabCompletion = value; }
        }

        /// <summary>
        /// The tab completed line.
        /// </summary>
        public static string TabCompletedLine
        {
            [DebuggerStepThrough]
            get { return tabCompletedLine; }
            [DebuggerStepThrough]
            set { tabCompletedLine = value; }
        }
        /// <summary>
        /// Updates the tab matches.
        /// </summary>
        /// <param name="matches">The list of matches.</param>
        /// <param name="matchedCommands">The corresponding commands.</param>
        [DebuggerStepThrough]
        public static void SetNewTabMatches(List<string> matches, List<Command> matchedCommands)
        {
            TabCompletion = true;
            TabMatchedCommands = matchedCommands;
            CurrentTabMatches = matches;
            TabCycle = 0;
            TabCompletedLine = matches[StandardConsole.TabCycle];
        }
        private static List<Command> matchedCommands;

        /// <summary>
        /// The currently matched commands.
        /// </summary>
        public static List<Command> TabMatchedCommands
        {
            [DebuggerStepThrough]
            get { return matchedCommands; }
            [DebuggerStepThrough]
            set { matchedCommands = value; }
        }

        private static int tabCycle;

        /// <summary>
        /// The current tab number.
        /// </summary>
        public static int TabCycle
        {
            [DebuggerStepThrough]
            get { return tabCycle; }
            [DebuggerStepThrough]
            set
            {

                tabCycle = value;
                if (currentTabMatches == null || currentTabMatches.Count == 0)
                {
                    return;
                }
                if (tabCycle >= 0)
                {
                    if (value < currentTabMatches.Count)
                    {
                        tabCompletedLine = currentTabMatches[tabCycle];
                    }
                    else
                    {
                        tabCycle = 0;
                        tabCompletedLine = currentTabMatches[tabCycle];
                    }
                }
                else
                {
                    tabCycle = currentTabMatches.Count - 1;
                    tabCompletedLine = currentTabMatches[tabCycle];
                }


            }
        }

        /// <summary>
        /// Writes a fail message.
        /// </summary>
        /// <param name="message">The message.</param>
        [DebuggerStepThrough]
        public static void Fail(string message)
        {
            Fail(message, null);
        }
        private static bool redirectInputToConsole;

        /// <summary>
        /// Whether to redirect input to the console.
        /// </summary>
        public static bool RedirectInputToConsole
        {
            [DebuggerStepThrough]
            get { return redirectInputToConsole; }
            [DebuggerStepThrough]
            set { redirectInputToConsole = value; }
        }
        /// <summary>
        /// Clears the tab completion state.
        /// </summary>
        public static void ResetTabCompletion()
        {
            StandardConsole.TabCompletion = false;
            StandardConsole.TabMatchedCommands = null;
            StandardConsole.CurrentTabMatches = null;
            StandardConsole.TabCompletedLine = null;
            StandardConsole.TabCycle = -1;
        }
        /// <summary>
        /// Sorts and writes a list of strings to the console.
        /// </summary>
        /// <param name="list">The list.</param>
        [DebuggerStepThrough]
        public static void OutputSortedList(List<string> list)
        {
            list.Sort();
            foreach (string line in list)
            {
                StandardConsole.WriteLine(line);
            }
        }
        private static int bottomViewLine;

        /// <summary>
        /// The offset of the bottom line.
        /// </summary>
        public static int BottomViewLineOffset
        {
            [DebuggerStepThrough]
            get { return bottomViewLine; }
            [DebuggerStepThrough]
            set { bottomViewLine = value; }
        }
        /// <summary>
        /// Shows the next tab completed line.
        /// </summary>
        public static void ShowNextTabCompletion()
        {
            List<Command> matchedCommands;
            List<string> matches = CommandParser.GetMatchingCommands(String.Concat("^", StandardConsole.InputLine.ToString()),false, out matchedCommands );
            if (matches.Count == 0)
            {
                StandardConsole.ResetTabCompletion();
            }
            else if (StandardConsole.CurrentTabMatches == null)
            {
                StandardConsole.SetNewTabMatches(matches, matchedCommands);
            }
            else if (matches.Count == StandardConsole.CurrentTabMatches.Count)
            {
                for (int i = 0; i < matches.Count; i++)
                {
                    if (matches[i] != StandardConsole.CurrentTabMatches[i])
                    {

                        StandardConsole.SetNewTabMatches(matches, matchedCommands);
                        break;
                    }
                }
                TabCycle = 0;
            }
            else
            {
                StandardConsole.SetNewTabMatches(matches, matchedCommands);
            }
        }
        /// <summary>
        /// Advances the tab cycle.
        /// </summary>
        [DebuggerStepThrough]
        public static void AdvanceTabCompletion()
        {
            if (TabCompletion)
            {
                TabCycle++;
            }
        }
        /// <summary>
        /// Lowers the tab cycle.
        /// </summary>
        [DebuggerStepThrough]
        public static void DecreaseTabCompletion()
        {
            if (TabCompletion)
            {
                TabCycle--; 
            }
        }
        private static StringBuilder inputLine = new StringBuilder();

        /// <summary>
        /// The input line.
        /// </summary>
        public static StringBuilder InputLine
        {
            [DebuggerStepThrough]
            get { return inputLine; }
            [DebuggerStepThrough]
            set { inputLine = value; }
        }


        private static bool consoleShowing = false;

        /// <summary>
        /// Whether the console is showing.
        /// </summary>
        public static bool ConsoleShowing
        {
            [DebuggerStepThrough]
            get { return consoleShowing; }
            [DebuggerStepThrough]
            set { consoleShowing = value; }
        }
        /// <summary>
        /// Reads the input line.
        /// </summary>
        [DebuggerStepThrough]
        public static void Read()
        {
            try
            {
                string command = inputLine.ToString();
                CommandParser.ProcessCommand(command);
            }
            catch (CommandFailedException e)
            {
                WriteLine(e.Commands[0] + ": Command not recognised.");
            }
        }
        /// <summary>
        /// Writes a fail message to the console.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="detailMessage">The detail message.</param>
        [DebuggerStepThrough]
        public static void Fail(string message, string detailMessage)
        {
            WriteLine(message, true);
            if (detailMessage != null)
            {
                WriteLine("", true);
                WriteLine(detailMessage, true);
            }
        }
        private static List<bool> isFailMessage = new List<bool>(1000);

        /// <summary>
        /// The list of fail messages.
        /// </summary>
        public static List<bool> IsFailMessage
        {
            [DebuggerStepThrough]
            get { return isFailMessage; }
        }

        private static List<string> transcript = new List<string>(1000);

        /// <summary>
        /// Gets or sets the transcript.
        /// </summary>
        /// <value>The transcript.</value>
        public static List<string> Transcript
        {
            [DebuggerStepThrough]
            get { return transcript; }
        }

        private static int inputHistoryPosition = -1;
        /// <summary>
        /// The position of the history cursor.
        /// </summary>
        public static int InputHistoryPosition
        {
            [DebuggerStepThrough]
            get { return inputHistoryPosition; }
            [DebuggerStepThrough]
            set { inputHistoryPosition = (int)MathHelper.Clamp(value, -1, inputHistory.Count - 1); }
        }
        /// <summary>
        /// Changes the history cursor position.
        /// </summary>
        /// <param name="i"></param>
        public static void ChangeInputToHistory(int i)
        {
            if (inputHistory.Count==0)
            {
                return;
            }
            StandardConsole.InputLine.Remove(0, StandardConsole.InputLine.Length);
            int position = inputHistory.Count - 1 - i;
            StandardConsole.InputLine.Append(StandardConsole.InputHistory[position]);
            StandardConsole.ResetTabCompletion();
        }

        private static List<string> inputHistory = new List<string>(100);
        /// <summary>
        /// The input history.
        /// </summary>
        public static List<string> InputHistory
        {
            [DebuggerStepThrough]
            get { return inputHistory; }
        }
        /// <summary>
        /// Writes a message to the console.
        /// </summary>
        /// <param name="message">The message.</param>
        [DebuggerStepThrough]
        public static void Write(string message)
        {
            transcript.Add(message);
        }

        /// <summary>
        /// Writes a message to the console, followed by a new line.
        /// </summary>
        /// <param name="message">The message.</param>
        [DebuggerStepThrough]
        public static void WriteLine(string message)
        {
            WriteLine(message, false);
        }
        /// <summary>
        /// Writes a message to the console, followed by a new line.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="isFail"> Whether it's a fail message.</param>
        [DebuggerStepThrough]
        private static void WriteLine(string message, bool isFail)
        {
            transcript.Add(message + Environment.NewLine);
            isFailMessage.Add(isFail);
        }
        private static Console defaultConsole;

        /// <summary>
        /// The default console.
        /// </summary>
        public static Console DefaultConsole
        {
            [DebuggerStepThrough]
            get { return defaultConsole; }
            [DebuggerStepThrough]
            set { defaultConsole = value; }
        }
    }
}